/**
 * create by zhike
 * date:2015年3月2日
 */
package com.acooly.module.openapi.client.provider.fbank;

import com.acooly.core.utils.Strings;
import com.acooly.module.openapi.client.api.AbstractApiServiceClient;
import com.acooly.module.openapi.client.api.exception.ApiClientException;
import com.acooly.module.openapi.client.api.exception.ApiServerException;
import com.acooly.module.openapi.client.api.marshal.ApiMarshal;
import com.acooly.module.openapi.client.api.marshal.ApiUnmarshal;
import com.acooly.module.openapi.client.api.transport.Transport;
import com.acooly.module.openapi.client.provider.fbank.domain.FbankApiMsgInfo;
import com.acooly.module.openapi.client.provider.fbank.domain.FbankNotify;
import com.acooly.module.openapi.client.provider.fbank.domain.FbankRequest;
import com.acooly.module.openapi.client.provider.fbank.domain.FbankResponse;
import com.acooly.module.openapi.client.provider.fbank.enums.FbankServiceEnum;
import com.acooly.module.openapi.client.provider.fbank.enums.FbankServiceTypeEnum;
import com.acooly.module.openapi.client.provider.fbank.marshall.*;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;

/**
 * ApiService执行器
 *
 * @author zhike
 */
@Component("fbankApiServiceClient")
@Slf4j
public class FbankApiServiceClient
        extends AbstractApiServiceClient<FbankRequest, FbankResponse, FbankNotify, FbankNotify> {


    public static final String PROVIDER_NAME = "fbank";

    @Resource(name = "fbankHttpTransport")
    private Transport transport;

    @Resource(name = "fbankRequestMarshall")
    private FbankRequestMarshall requestMarshal;

    @Resource(name = "fbankRedirectMarshall")
    private FbankRedirectMarshall redirectMarshal;

    @Resource(name = "fbankSignMarshall")
    private FbankSignMarshall signMarshall;

    @Autowired
    private FbankResponseUnmarshall responseUnmarshal;
    @Autowired
    private FbankNotifyUnmarshall notifyUnmarshal;

    @Autowired
    private OpenAPIClientFbankProperties openAPIClientFbankProperties;

    @Override
    public FbankResponse execute(FbankRequest request) {
        try {
            beforeExecute(request);
            String url = getRequestUrl(request);
            String requestMessage = getRequestMarshal().marshal(request);
            log.info("请求密文报文: {}", requestMessage);
            String responseMessage = getTransport().exchange(requestMessage, url);
            String serviceKey = getServiceKey(request);
            Map<String,String> respMessage = Maps.newHashMap();
            respMessage.put(FbankConstants.PARTNER_KEY,request.getMchntId());
            respMessage.put(FbankConstants.RESP_MESSAGE_KEY,responseMessage);
            FbankResponse t = responseUnmarshal.unmarshal(respMessage, serviceKey);
            t.setService(request.getService());
            afterExecute(t);
            return t;
        } catch (ApiServerException ose) {
            log.error("服务器:" + ose.getMessage(), ose);
            throw ose;
        } catch (ApiClientException oce) {
            log.error("客户端:" + oce.getMessage(), oce);
            throw oce;
        } catch (Exception e) {
            log.error("内部错误:" + e.getMessage(), e);
            throw new ApiClientException("内部错误:" + e.getMessage());
        }
    }

    public FbankNotify notice(HttpServletRequest request, String serviceKey) {
        try {
            Map<String, String> notifyData = getSignMarshall().getDateMap(request);
            FbankNotify notify = getNoticeUnmarshal().unmarshal(notifyData, serviceKey);
            afterNotice(notify);
            return notify;
        } catch (ApiClientException oce) {
            log.warn("客户端:{}", oce.getMessage());
            throw oce;
        } catch (Exception e) {
            log.warn("内部错误:{}", e.getMessage());
            throw new ApiClientException("内部错误:" + e.getMessage());
        }
    }

    @Override
    protected ApiMarshal<String, FbankRequest> getRequestMarshal() {
        return this.requestMarshal;
    }

    @Override
    protected ApiUnmarshal<FbankResponse, String> getResponseUnmarshal() {
        return null;
    }

    @Override
    protected ApiUnmarshal<FbankNotify, Map<String, String>> getNoticeUnmarshal() {
        return this.notifyUnmarshal;
    }

    @Override
    protected ApiMarshal<String, FbankRequest> getRedirectMarshal() {
        return this.redirectMarshal;
    }

    @Override
    protected Transport getTransport() {
        return this.transport;
    }

    @Override
    protected ApiUnmarshal<FbankNotify, Map<String, String>> getReturnUnmarshal() {
        return this.notifyUnmarshal;
    }

    public FbankSignMarshall getSignMarshall() {
        return signMarshall;
    }

    public void setSignMarshall(FbankSignMarshall signMarshall) {
        this.signMarshall = signMarshall;
    }

    @Override
    protected void beforeExecute(FbankRequest request) {
        super.beforeExecute(request);
        request.setMchntId(openAPIClientFbankProperties.getPartnerId());
    }

    /**
     * 获取请求地址
     * @param request
     * @return
     */
    private String getRequestUrl(FbankRequest request) {
        if (Strings.isBlank(request.getGatewayUrl())) {
            FbankApiMsgInfo fbankApiMsgInfo = request.getClass().getAnnotation(FbankApiMsgInfo.class);
            if (fbankApiMsgInfo == null) {
                throw new ApiClientException("请求响应实体应标注FbankApiMsgInfo注解");
            } else {
                FbankServiceTypeEnum serviceType = fbankApiMsgInfo.serviceType();
                FbankServiceEnum service = fbankApiMsgInfo.service();
                if (serviceType == FbankServiceTypeEnum.mer) {
                    return openAPIClientFbankProperties.getMerGatewayUrl() + service.getCode();
                } else if (serviceType == FbankServiceTypeEnum.payol) {
                    return openAPIClientFbankProperties.getPayolGatewayUrl() + service.getCode();
                } else {
                    throw new ApiClientException("不支持此服务类型:" + serviceType.getCode());
                }
            }
        } else {
            return request.getGatewayUrl();
        }
    }

    /**
     * 获取服务唯一标识
     * @param request
     * @return
     */
    private String getServiceKey(FbankRequest request) {
        FbankApiMsgInfo fbankApiMsgInfo = request.getClass().getAnnotation(FbankApiMsgInfo.class);
        FbankServiceEnum service = fbankApiMsgInfo.service();
        return service.getKey();
    }

    @Override
    public String getName() {
        return PROVIDER_NAME;
    }
}
